package com.algebra.util.encrypt;

import cn.hutool.core.collection.CollectionUtil;
import com.algebra.util.annotation.FieldEncrypt;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;

/**
 * @author al
 * @date 2022/5/24 16:05
 * @description
 */
public class DataFilter {

    private static boolean flag = true;
    private static Map<Class<?>, List<FieldSetProperty>> fieldSetPropertyMap = new ConcurrentHashMap<>(16);
    private static Set<Class<?>> classSet  = new CopyOnWriteArraySet<>();


    public static List<FieldSetProperty> getFieldSetProperty(Class<?> clazz) throws Exception {
        if (classSet.contains(clazz)) {
            return null;
        } else {
            List<FieldSetProperty> result = fieldSetPropertyMap.get(clazz);
            if (null == result) {
                if (clazz.isAssignableFrom(HashMap.class)) {
                    classSet.add(clazz);
                } else {
                    result = new ArrayList<>();
                    List<Field> fields = getObjectField(clazz);
                    Iterator<Field> fieldIterator = fields.iterator();
                    while (true) {
                        Field field;
                        FieldEncrypt fieldEncrypt;
                        do {
                            if (!fieldIterator.hasNext()) {
                                if (result.isEmpty()) {
                                    classSet.add(clazz);
                                } else {
                                    fieldSetPropertyMap.put(clazz, result);
                                }
                                return result;
                            }
                            field = fieldIterator.next();
                            fieldEncrypt = null;
                            if (flag) {
                                fieldEncrypt = field.getAnnotation(FieldEncrypt.class);
                                if (null != fieldEncrypt && !field.getType().isAssignableFrom(String.class)) {
                                    throw new Exception("annotation `@FieldEncrypt` only string types are supported.");
                                }
                            }
                        } while (null == fieldEncrypt);

                        result.add(new FieldSetProperty(field.getName(), fieldEncrypt));
                    }
                }
            }
            return result;
        }
    }


    public static boolean resultSet(Configuration configuration, Object obj, BiConsumer<MetaObject, FieldSetProperty> var2) throws Exception {
        List<FieldSetProperty> fieldSetProperties = getFieldSetProperty(obj.getClass());
        if (!CollectionUtil.isEmpty(fieldSetProperties)) {
            MetaObject metaObject = configuration.newMetaObject(obj);
            fieldSetProperties.parallelStream().forEach((var2x) -> {
                var2.accept(metaObject, var2x);
            });
            return true;
        } else {
            return false;
        }
    }

    public static boolean insertOrUpdate(Configuration configuration, Object obj, BiConsumer<MetaObject, FieldSetProperty> var2) throws Exception {
        List<FieldSetProperty> fieldSetProperties = getFieldSetProperty(obj.getClass());
        if (!CollectionUtil.isEmpty(fieldSetProperties) && fieldSetProperties.stream().anyMatch((var0x) -> {
            return null != var0x.getFieldEncrypt();
        })) {
            MetaObject var4 = configuration.newMetaObject(obj);
            fieldSetProperties.stream().filter((var0x) -> {
                return null != var0x.getFieldEncrypt();
            }).forEach((var2x) -> {
                var2.accept(var4, var2x);
            });
            return true;
        } else {
            return false;
        }
    }

    /**
     * 获取对象属性
     * @param clazz
     * @return
     */
    public static List<Field> getObjectField(Class<?> clazz) {
        if (null == clazz) {
            return null;
        } else {
            Field[] fields = clazz.getDeclaredFields();
            List<Field> fieldList = Arrays.stream(fields)
                    .filter((var0x) -> !Modifier.isStatic(var0x.getModifiers()))
                    .filter((var0x) -> !Modifier.isTransient(var0x.getModifiers())).collect(Collectors.toList());
            Class<?> var3 = clazz.getSuperclass();
            if (var3.equals(Object.class)) {
                return fieldList;
            } else {
                // 父类属性获取
                List<Field> parFields = getObjectField(var3);
                Iterator<Field> parFieldsIter = parFields.iterator();
                while (parFieldsIter.hasNext()) {
                    Field parField = parFieldsIter.next();
                    if (fieldList.stream().noneMatch((var1x) -> var1x.getName().equals(parField.getName()))) {
                        fieldList.add(parField);
                    }
                }
                return fieldList;
            }
        }
    }

}
